package com.amon.canary;

import android.os.Debug;
import android.os.Environment;
import android.util.Log;

import com.android.tools.perflib.captures.DataBuffer;
import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
import com.squareup.haha.perflib.ClassInstance;
import com.squareup.haha.perflib.ClassObj;
import com.squareup.haha.perflib.Instance;
import com.squareup.haha.perflib.RootObj;
import com.squareup.haha.perflib.Snapshot;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;

import gnu.trove.THashMap;
import gnu.trove.TObjectProcedure;

public class HeapAnalyzer {
    public void analysisLeak(String key) {
        File file = new File(Environment.getDownloadCacheDirectory().getAbsolutePath(), "test.hprof");
        try {
            Debug.dumpHprofData(file.getAbsolutePath());
            DataBuffer buffer = new MemoryMappedFileBuffer(file);
            Snapshot snapshot = Snapshot.createSnapshot(buffer);
            Log.e("TAG", "" + snapshot.getGCRoots().size());
            deduplicateGcRoots(snapshot);
            Log.e("TAG", "" + snapshot.getGCRoots().size());
            Instance leakInstance = findLeakingReference(snapshot, key);
            if (leakInstance != null) {
                Log.e("TAG", "泄漏的instance:" + leakInstance);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private Instance findLeakingReference(Snapshot snapshot, String key) {
        ClassObj refClass = snapshot.findClass(KeyWeakReference.class.getName());
        if (refClass == null) {
            return null;
        }
        for (Instance instance : refClass.getInstancesList()) {
            List<ClassInstance.FieldValue> values = HaHaHelper.classInstanceValues(instance);
            Object keyFiledValue = HaHaHelper.filedValue(values, "key");
            String keyCandidate = HaHaHelper.asString(keyFiledValue);
            if (keyCandidate.equals(key)) {
                return instance;
            }
        }
        return null;
    }

    private void deduplicateGcRoots(Snapshot snapshot) {
        THashMap<String, RootObj> uniqueRootMap = new THashMap<>();
        Collection<RootObj> gcRoots = snapshot.getGCRoots();
        for (RootObj gcRoot : gcRoots) {
            String key = generateRootKey(gcRoot);
            if (!uniqueRootMap.containsKey(key)) {
                uniqueRootMap.put(key, gcRoot);
            }
        }
        gcRoots.clear();

        uniqueRootMap.forEach(key -> gcRoots.add(uniqueRootMap.get(key)));
    }

    private String generateRootKey(RootObj gcRoot) {
        return String.format("%s@0x%08x", gcRoot.getRootType().getName(), gcRoot.getId());
    }
}
